﻿// <copyright file="PermissionListEditor.cs" company="Sinclair Community College">
// Copyright (c) Sinclair Community College. All rights reserved.
// </copyright>
namespace SinclairCC.AppVSuite.PermissionCopy
{
    using System;
    using System.Collections.Generic;
    using System.Data;
    using System.Text;
    using System.Windows.Forms;
    using SinclairCC.AppVSuite.Shared;

    /// <summary>
    /// This class represents a form that lets the user edit the list of security groups that will be given
    /// access to an application or application group.
    /// </summary>
    internal partial class PermissionListEditor : Form
    {
        /// <summary>
        /// A connection to the App-V database.
        /// </summary>
        private System.Data.SqlClient.SqlConnection connAppV;

        /// <summary>
        /// The tag data from the TreeView node that represents a application or application group.
        /// </summary>
        private TreeNodeTag selectedNodeTag;

        /// <summary>
        /// The list of security groups that will be granted access to the destination application or application group.
        /// </summary>
        private System.Collections.ObjectModel.Collection<string> selectedGroups;

        /// <summary>
        /// Initializes a new instance of the PermissionListEditor class.
        /// </summary>
        /// <param name="selectedTreeNodeTag">
        /// The tag data from the TreeView node that represents an application or application group.
        /// </param>
        public PermissionListEditor(TreeNodeTag selectedTreeNodeTag)
        {
            this.InitializeComponent();

            this.selectedGroups = new System.Collections.ObjectModel.Collection<string>();
            this.selectedNodeTag = selectedTreeNodeTag;
        }

        /// <summary>
        /// Gets a list of the SIDs of the security groups that should be granted access to the
        /// destination application or application group.
        /// </summary>
        public System.Collections.ObjectModel.Collection<string> SelectedGroups
        {
            get { return this.selectedGroups; }
        }

        /// <summary>
        /// Gets a value indicating whether the destination application's access assignments
        /// should be deleted and replaced by the security groups specified on this form.
        /// </summary>
        public bool ReplaceExistingGroups
        {
            get { return this.replacePermissionsCheckBox.Checked; }
        }

        /// <summary>
        /// Handles the Click event for the OK button.
        /// </summary>
        /// <param name="sender">
        /// The button being clicked.
        /// </param>
        /// <param name="e">
        /// Data specific to the event.
        /// </param>
        private void ClickOKButton(object sender, EventArgs e)
        {
            for (int i = 0; i < this.groupList.Items.Count; i++)
            {
                this.selectedGroups.Add((string)this.groupList.Items[i].Tag);
            }

            this.Close();
        }
        
        /// <summary>
        /// Handles the Closed event for the form.
        /// </summary>
        /// <param name="sender">
        /// The form that has been closed.
        /// </param>
        /// <param name="e">
        /// Data specific to this event.
        /// </param>
        private void FormClosedEvent(object sender, FormClosedEventArgs e)
        {
            if (this.connAppV != null)
            {
                if (this.connAppV.State == ConnectionState.Open)
                {
                    this.connAppV.Close();
                }

                this.connAppV.Dispose();
            }
        }

        /// <summary>
        /// Populate the ListView with the list of security groups to which the application(s) is/are assigned.
        /// </summary>
        private void InitializeListView()
        {
            this.groupList.BeginUpdate();
            this.groupList.Items.Clear();
            this.groupList.Sorting = SortOrder.Ascending;

            if (this.selectedNodeTag.Type == AppVObjectType.ApplicationGroup)
            {
                DataTable appsTable = AppVDatabaseFunctions.GetApplications(this.connAppV, this.selectedNodeTag.ID, true);
                List<int> apps = new List<int>(appsTable.Rows.Count);
                foreach (DataRow row in appsTable.Rows)
                {
                    int app_id = (int)row["app_id"];
                    if (!apps.Contains(app_id))
                    {
                        apps.Add(app_id);
                    }
                }

                this.AddListViewItems(apps);
            }
            else
            {
                this.AddListViewItems(this.selectedNodeTag.ID);
            }

            this.groupList.Sort();
            this.groupList.EndUpdate();

            this.ToggleControlStates();
        }

        /// <summary>
        /// Adds the assigned security groups for a single application to the ListView.
        /// </summary>
        /// <param name="appId">
        /// The ID value for the application.
        /// </param>
        private void AddListViewItems(int appId)
        {
            this.AddListViewItems(new List<int>() { appId });
        }

        /// <summary>
        /// Adds the assigned security groups for multiple applications to the ListView.
        /// </summary>
        /// <param name="appIds">
        /// The ID values for the applications.
        /// </param>
        private void AddListViewItems(List<int> appIds)
        {
            // Get the distinct SIDs for the security groups to which each application is assigned.
            List<string> group_refs = new List<string>();
            foreach (int i in appIds)
            {
                string[] assignedGroups = AppVDatabaseFunctions.GetApplicationAssignments(this.connAppV, i);
                foreach (string group_ref in assignedGroups)
                {
                    if (!group_refs.Contains(group_ref))
                    {
                        group_refs.Add(group_ref);
                    }
                }
            }

            // For each security group SID, add an item to the ListView containing the security group's name and domain.
            foreach (string group_ref in group_refs)
            {
                System.DirectoryServices.DirectoryEntry groupDe = new System.DirectoryServices.DirectoryEntry(string.Format(System.Globalization.CultureInfo.InvariantCulture, "LDAP://<SID={0}>", group_ref));
                string groupName = (string)groupDe.Properties["name"][0];
                groupDe.Dispose();

                System.Security.Principal.SecurityIdentifier sid = new System.Security.Principal.SecurityIdentifier(group_ref);
                System.DirectoryServices.DirectoryEntry domainDe = new System.DirectoryServices.DirectoryEntry(string.Format(System.Globalization.CultureInfo.InvariantCulture, "LDAP://<SID={0}>", sid.AccountDomainSid.ToString()));
                string domainName = (string)domainDe.Properties["name"][0];
                domainName = domainName.ToUpperInvariant();
                domainDe.Dispose();

                ListViewItem newItem = new ListViewItem(new string[] { groupName, domainName });
                newItem.Tag = group_ref;
                this.groupList.Items.Add(newItem);
            }
        }

        /// <summary>
        /// Handles the Click event for the Cancel button.
        /// </summary>
        /// <param name="sender">
        /// The button being clicked.
        /// </param>
        /// <param name="e">
        /// Data specific to the event.
        /// </param>
        private void ClickCancelButton(object sender, EventArgs e)
        {
            this.Close();
        }

        /// <summary>
        /// Handles the SelectedIndexChanged event for the ListView.
        /// </summary>
        /// <param name="sender">
        /// The ListView whose SelectedIndex has changed.
        /// </param>
        /// <param name="e">
        /// Data specific to this event.
        /// </param>
        private void GroupListSelectedIndexChanged(object sender, EventArgs e)
        {
            this.ToggleControlStates();
        }

        /// <summary>
        /// Sets the Enabled state of some of the form's controls.
        /// </summary>
        private void ToggleControlStates()
        {
            this.deleteButton.Enabled = this.groupList.SelectedItems.Count > 0;
        }

        /// <summary>
        /// Handles the Click event for the Delete button.
        /// </summary>
        /// <param name="sender">
        /// The button being clicked.
        /// </param>
        /// <param name="e">
        /// Data specific to the event.
        /// </param>
        private void ClickDeleteButton(object sender, EventArgs e)
        {
            DialogResult promptReponse = MessageBox.Show(this, string.Format(System.Globalization.CultureInfo.CurrentCulture, "Are you sure you want to delete the selected item{0}?", this.groupList.SelectedItems.Count == 1 ? string.Empty : "s"), "App-V Permission Copy", MessageBoxButtons.YesNo, MessageBoxIcon.Question, MessageBoxDefaultButton.Button2, 0);

            if (promptReponse == System.Windows.Forms.DialogResult.Yes)
            {
                foreach (ListViewItem item in this.groupList.SelectedItems)
                {
                    this.groupList.Items.Remove(item);
                }
            }

            this.ToggleControlStates();
        }

        /// <summary>
        /// Handles the Load event for the form.
        /// </summary>
        /// <param name="sender">
        /// The form being loaded.
        /// </param>
        /// <param name="e">
        /// Data specific to the event.
        /// </param>
        private void FormLoadEvent(object sender, EventArgs e)
        {
            this.EnsureConnectionStringExists();
            this.InitializeListView();
        }

        /// <summary>
        /// Ensures the application has the information it needs to connect to the App-V database.
        /// </summary>
        private void EnsureConnectionStringExists()
        {
            // Read the connection string from the registry, prompting the user for any missing information.
            DialogResult connectionPropertiesResult = System.Windows.Forms.DialogResult.None;
            string connectionString = null;
            do
            {
                try
                {
                    connectionString = Settings.ConnectionString;
                }
                catch (System.ArgumentNullException)
                {
                    connectionString = null;
                }

                if (connectionString == null)
                {
                    connectionPropertiesResult = new ConnectionProperties().ShowDialog(this);
                }
            }
            while ((connectionString == null) && (connectionPropertiesResult != System.Windows.Forms.DialogResult.Cancel));

            // If the connection string is successfully obtained from the registry, connect to the
            // App-V database and initialize the form's ListView control. Otherwise, close the form.
            if (connectionString != null)
            {
                this.connAppV = new System.Data.SqlClient.SqlConnection(connectionString);
                try
                {
                    this.connAppV.Open();
                }
                catch (System.Data.SqlClient.SqlException sqlError)
                {
                    StringBuilder errorMessage = new StringBuilder("An SQL error occurred while opening a connection to the database.");
                    if (sqlError.Message.Length > 0)
                    {
                        errorMessage.Append(System.Environment.NewLine);
                        errorMessage.Append(System.Environment.NewLine);
                        errorMessage.Append(sqlError.Message);
                    }

                    MessageBox.Show(this, errorMessage.ToString(), "App-V Permission Copy", MessageBoxButtons.OK, MessageBoxIcon.Error, MessageBoxDefaultButton.Button1, 0);
                }
                catch (System.InvalidOperationException invalidOpError)
                {
                    StringBuilder errorMessage = new StringBuilder("An invalid operation error occurred while opening a connection to the database.");
                    if (invalidOpError.Message.Length > 0)
                    {
                        errorMessage.Append(System.Environment.NewLine);
                        errorMessage.Append(System.Environment.NewLine);
                        errorMessage.Append(invalidOpError.Message);
                    }

                    MessageBox.Show(this, errorMessage.ToString(), "App-V Permission Copy", MessageBoxButtons.OK, MessageBoxIcon.Error, MessageBoxDefaultButton.Button1, 0);
                }
            }
            else
            {
                this.Close();
            }
        }
    }
}
